package org.example.chat.websocket.nettyhandler;

import cn.hutool.json.JSONUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;
import org.example.chat.security.components.utils.JWTUtils;
import org.example.chat.security.entity.LoginUser;
import org.example.chat.websocket.entity.MessagePack;
import org.example.chat.websocket.entity.MessageType;
import org.example.chat.websocket.entity.ChannelContext;
import org.example.chat.websocket.messagehandler.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 自定义文本帧消息处理器
 * SimpleChannelInboundHandler : 这个入栈处理器可以指定消息类型
 * @author cheval
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    @Autowired
    private JWTUtils jwtUtils;
    @Autowired
    private ChatMessageHandler chatMessageHandler;
    @Autowired
    private SignMessageHandler signMessageHandler;
    @Autowired
    private ChannelContext channelContext;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        String text = textWebSocketFrame.text();
        // 1. 消息反序列化
        MessagePack messagePack = null;
        try {
            messagePack = JSONUtil.toBean(text, MessagePack.class);
        } catch (Exception e) {
            log.error("消息格式不满足 MessagePack.class的json字符串格式, 消息不予处理");
            return;
        }

        if (messagePack == null) {
            return;
        }
        // 2. 消息处理， 策略模式
        MessageHandler messageHandler = null;
        if (MessageType.CHAT.getType().equals(messagePack.getType())) {
            messageHandler = chatMessageHandler;
        } else if (MessageType.SIGN.getType().equals(messagePack.getType())) {
            messageHandler = signMessageHandler;
        } else if (MessageType.HEART.getType().equals(messagePack.getType())) {
            return;
        }
        MessageHandlerContext messageHandlerContext = new MessageHandlerContext(messageHandler, messagePack, channelHandlerContext.channel());
        messageHandlerContext.handler();
    }


    /**
     * ws客户端调用close函数后或服务端调用close方法后执行一次
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        channelContext.destroy(ctx.channel());
    }

    /**
     * 通讯过程中(例如channelRead0方法出现异常)异常发生后的处理
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        channelContext.destroy(ctx.channel());
    }

    /**
     * 客户端连接后会触发一个握手完成事件WebSocketServerProtocolHandler$HandshakeComplete
     * 这个方法可以用于对连接客户端的认证，以及认证之后的初始化操作
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            Channel channel = ctx.channel();
            WebSocketServerProtocolHandler.HandshakeComplete handshakeCompleteEvent = (WebSocketServerProtocolHandler.HandshakeComplete) evt;
            // 1. 获取请求uri
            String uri = handshakeCompleteEvent.requestUri();
            // 2. 获取token
            String token = getToken(uri);
            if (token == null) {
                // 没有token, 断开连接
                channel.close();
                return;
            }
            // 3. 判断token是否有效
            boolean valid = jwtUtils.isValid(token);
            if (!valid) {
                // 无效token, 断开连接
                channel.close();
                return;
            }
            // 4. 从token中获取用户id
            LoginUser loginUser = (LoginUser) jwtUtils.getUserInfoFromToken(token);
            String userId = loginUser.getUser().getId();
            if (userId == null) {
                // 无效token， 断开连接
                channel.close();
                return;
            }
            // 5. 初始化channel上下文
            channelContext.initChannelContext(channel, userId);
        }
    }

    private String getToken(String uri) {
        if (uri == null || "".equals(uri.trim())) {
            return null;
        }
        String[] arr = uri.split("\\?token=");
        if (arr == null || arr.length < 2) {
            return null;
        }
        return arr[1];
    }
}
